package client

import (
	"bgs/conf"
	"bgs/grpc/gen/proxy2/account"
	"bgs/grpc/gen/proxy2/bilinadmin"
	bilinprod "bgs/grpc/gen/proxy2/bilinprod"
	blingpromotion "bgs/grpc/gen/proxy2/blingpromotion"
	"bgs/grpc/gen/proxy2/inventory"
	"bgs/grpc/gen/proxy2/wxpay"
	"bgs/util"
	"context"
	// "encoding/json"
	"fmt"

	grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware"
	"go.uber.org/zap"
	"google.golang.org/grpc"
	"google.golang.org/grpc/metadata"
	"google.golang.org/grpc/status"
	"google.golang.org/protobuf/types/known/emptypb"
	// "google.golang.org/grpc/codes"
)

// GrpcClientProxy proxy wxpay inventory grpc service
type GrpcClientProxy struct {
	slog         *zap.SugaredLogger
	vars         *conf.VarsSection
	AccountCl    account.LdAccountClient
	BilinAdminCl bilinadmin.BilinAdminClient
	EmployeeCl   bilinadmin.BilinEmployeeClient
	BilinProdCl  bilinprod.ProdClient
	BPOffCl      blingpromotion.OffOpServiceClient
	BPCouponCl   blingpromotion.CouponOpServiceClient
	InventoryCl  inventory.InventoryClient
	WxpayCl      wxpay.WxpayClient
}

// NewGrpcClientProxy initialization
func NewGrpcClientProxy(
	slog *zap.SugaredLogger,
	vars *conf.VarsSection,
	accountConf conf.AccountSection,
	bilinAdminConf conf.BilinAdminSection,
	bilinProdConf conf.BilinProdSection,
	blingPromotionConf conf.BlingPromotionSection,
	inventoryConf conf.InventorySection,
	wxpayConf conf.WxpaySection,
) *GrpcClientProxy {

	accountAddress := fmt.Sprintf("%s:%s", accountConf.Host, accountConf.Port)
	accountConn, err := grpc.Dial(accountAddress, grpc.WithInsecure(), grpc.WithBlock())
	if err != nil {
		slog.Fatalf("account connect err: %v", err)
	}
	// defer inventoryConn.Close()
	slog.Infof("accountConn ok=>%s", accountAddress)

	bilinAdminAddress := fmt.Sprintf("%s:%s", bilinAdminConf.Host, bilinAdminConf.Port)
	bilinAdminConn, err := grpc.Dial(bilinAdminAddress, grpc.WithInsecure(), grpc.WithBlock())
	if err != nil {
		slog.Fatalf("bilinAdmin connect err: %v", err)
	}
	// defer bilinAdminConn.Close()
	slog.Infof("bilinAdminConn ok=>%s", bilinAdminAddress)

	bilinProdAddress := fmt.Sprintf("%s:%s", bilinProdConf.Host, bilinProdConf.Port)
	bilinProdConn, err := grpc.Dial(bilinProdAddress, grpc.WithInsecure(), grpc.WithBlock())
	if err != nil {
		slog.Fatalf("bilinProd connect err: %v", err)
	}
	// defer bilinProdConn.Close()
	slog.Infof("bilinProdConn ok=>%s", bilinProdAddress)

	blingPromotionAddress := fmt.Sprintf("%s:%s", blingPromotionConf.Host, blingPromotionConf.Port)
	blingPromotionConn, err := grpc.Dial(blingPromotionAddress,
		grpc.WithUnaryInterceptor(
			grpc_middleware.ChainUnaryClient(
				UnaryBilinTokenInterceptor(slog, vars, func(ctx context.Context, fullMethodName string) bool {
					return !util.Contains(fullMethodName, "/Ping")
				}),
				UnaryPagerInterceptor(slog, vars, func(ctx context.Context, fullMethodName string) bool {
					return util.Contains(fullMethodName, "/List")
				}),
			),
		), grpc.WithInsecure(), grpc.WithBlock())
	if err != nil {
		slog.Fatalf("blingPromotion connect err: %v", err)
	}
	// defer bilinProdConn.Close()
	slog.Infof("blingPromotion ok=>%s", blingPromotionAddress)

	inventoryAddress := fmt.Sprintf("%s:%s", inventoryConf.Host, inventoryConf.Port)
	inventoryConn, err := grpc.Dial(inventoryAddress, grpc.WithInsecure(), grpc.WithBlock())
	if err != nil {
		slog.Fatalf("inventory connect err: %v", err)
	}
	// defer inventoryConn.Close()
	slog.Infof("inventoryConn ok=>%s", inventoryAddress)

	wxpayAddress := fmt.Sprintf("%s:%s", wxpayConf.Host, wxpayConf.Port)
	wxpayConn, err := grpc.Dial(wxpayAddress, grpc.WithInsecure(), grpc.WithBlock())
	if err != nil {
		slog.Fatalf("wxpay connect err: %v", err)
	}
	// defer wxpayConn.Close()
	slog.Infof("wxpayConn ok=>%s", wxpayAddress)

	accountCl := account.NewLdAccountClient(accountConn)
	bilinAdminCl := bilinadmin.NewBilinAdminClient(bilinAdminConn)
	employeeCl := bilinadmin.NewBilinEmployeeClient(bilinAdminConn)
	bilinProdCl := bilinprod.NewProdClient(bilinProdConn)

	bpOffCl := blingpromotion.NewOffOpServiceClient(blingPromotionConn)
	pingOffRes, err := bpOffCl.PingOff(context.Background(), &emptypb.Empty{})
	if err != nil {
		slog.Infof("bpOffCl.PingOff=>%+v", err)
		return nil
	}
	slog.Infof("bpOffCl.PingOff=>%s", pingOffRes.Msg)

	bpCouponCl := blingpromotion.NewCouponOpServiceClient(blingPromotionConn)
	pingCouponRes, err := bpCouponCl.PingCoupon(context.Background(), &emptypb.Empty{})
	if err != nil {
		slog.Infof("bpCouponCl.PingCoupon=>%+v", err)
		return nil
	}
	slog.Infof("bpCouponCl.PingCoupon=>%+v", pingCouponRes.Msg)

	inventoryCl := inventory.NewInventoryClient(inventoryConn)
	wxpayCl := wxpay.NewWxpayClient(wxpayConn)
	slog.Infof("wxpay inventory account client ready")

	proxy := &GrpcClientProxy{
		slog:         slog,
		vars:         vars,
		AccountCl:    accountCl,
		BilinAdminCl: bilinAdminCl,
		EmployeeCl:   employeeCl,
		BilinProdCl:  bilinProdCl,
		BPOffCl:      bpOffCl,
		BPCouponCl:   bpCouponCl,
		InventoryCl:  inventoryCl,
		WxpayCl:      wxpayCl,
	}

	//directly testing meta
	// token := &util.BilinToken{
	// 	GID:     "gid_1",
	// 	BrandId: 1,
	// 	EmpId:   101,
	// 	Name:    "tester",
	// }
	// tokenBytes, err := json.Marshal(token)
	// if err != nil {
	// 	return nil
	// }
	// ctx, cancel1 := proxy.AppendMetadataContextWith(context.Background(), "btoken", string(tokenBytes))
	// defer cancel1()

	ctx, cancel2 := proxy.AppendMetadataContextWith(context.Background(), "page_no", "2")
	defer cancel2()

	res, err := bpCouponCl.ListCouponWithStats(ctx, &blingpromotion.ListCouponWithStatsReq{
		CouponStatuses: []string{"published"},
		OfferTypes:       []string{"discount"},
	})
	if err != nil {
		slog.Infof("bpCouponCl.ListCouponWithStats=>%+v", err)
		return nil
	}
	slog.Infof("bpCouponCl.ListCouponWithStats=>%+v", res.CouponWithStats)

	return proxy
}

// GetInventoryErrorInfo 获取错误类型(仓库)
func (p *GrpcClientProxy) GetInventoryErrorInfo(err error) *inventory.DefaultResp {
	if s, ok := status.FromError(err); ok && s != nil {
		// if (s.Code() == codes.DeadlineExceeded) {

		// }
		for _, d := range s.Details() {

			switch t := d.(type) {
			case *inventory.DefaultResp:
				return t

			default:
				fmt.Printf("s:%v", s)
			}

		}
	}
	return nil
}

// GetBilinProdErrorInfo 获取错误类型(商品)
func (p *GrpcClientProxy) GetBilinProdErrorInfo(err error) *bilinprod.DefaultResp {
	if s, ok := status.FromError(err); ok && s != nil {
		// if (s.Code() == codes.DeadlineExceeded) {

		// }
		for _, d := range s.Details() {

			switch t := d.(type) {
			case *bilinprod.DefaultResp:
				return t

			default:
				fmt.Printf("s:%v", s)
			}

		}
	}
	return nil
}

// NewContextWith 获取调用远程grpc的ctx
func (p *GrpcClientProxy) NewContextWith(ctx context.Context) (ctxWithTimout context.Context, cancel context.CancelFunc) {
	return appendMetadataKeyValContext(ctx, p.vars.GrpcTimeout, "", "")
}

// NewTokenContextWith 获取调用远程grpc的ctx with token
func (p *GrpcClientProxy) AppendMetadataContextWith(ctx context.Context, key, val string) (ctxWithTimout context.Context, cancel context.CancelFunc) {
	return appendMetadataKeyValContext(ctx, p.vars.GrpcTimeout, key, val)
}

func appendMetadataKeyValContext(ctx context.Context, timeout uint8, key, val string) (newCtx context.Context, cancel context.CancelFunc) {
	ctxWithTimout, cancel := context.WithTimeout(ctx, util.Uint8ToSecondDuration(timeout))
	if !util.IsEmpty(key) && !util.IsEmpty(val) {
		newCtx = metadata.AppendToOutgoingContext(ctxWithTimout, key, val)
	} else {
		newCtx = ctxWithTimout
	}
	return
}
